﻿/*
 * Created by SharpDevelop.
 * User: v-edwild
 * Date: 04/07/2007
 * Time: 17:21
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */

using System;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;
using WatiN.Core;
using Wax.Properties;

namespace Wax
{
	/// <summary>
	/// Validates a given property of an <see cref="WatiN.Core.Element">element</see> has an expected value.
	/// </summary>
	public class ValidateCommand : PropertyCommand
	{
        /// <summary>
        /// The expected values of the property being validated.
        /// </summary>
	    private Collection<string> expectedValues;

	    #region Public properties
		
		/// <summary>
		/// The expected value of the property being validated.
		/// </summary>
		public string ExpectedValue
		{
			get { return this.Arguments[0]; }
		}

        /// <summary>
        /// Gets a value indicating whether the <see cref="ExpectedValue"/> is regular expression.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if the <see cref="ExpectedValue"/> is regular expression; otherwise, <c>false</c>.
        /// </value>
	    public bool IsRegularExpression
	    {
	        get
	        {
	            if (this.ExpectedValue.StartsWith("*", StringComparison.OrdinalIgnoreCase) ||
                    this.ExpectedValue.Contains(@"\w"))
	            {
	                return true;
	            }
                else
	            {
	                return false;
	            }
	        }
	    }

        /// <summary>
        /// The expected values of the property being validated.
        /// </summary>
        public Collection<string> ExpectedValues
        {
            get
            {
                if (this.expectedValues == null)
                {
                    this.expectedValues = new Collection<string>();

                    for (int i = 0; i < this.Arguments.Count - 1; i++)
                    {
                        this.expectedValues.Add(this.Arguments[i]);
                    }
                }

                return this.expectedValues;
            }
        }

		#endregion
				
		#region Public instance methods
		
		/// <summary>
		/// Executes this command
		/// </summary>
		/// <param name="context">The execution context used to execute this command</param>
		[CLSCompliant(false)]
		public override void Execute(IExecutionContext context)
		{
            switch(this.CommandType)
            {
                case CommandType.Validate:
                    ExecuteSingleItemValidation(context);
                    break;
                case CommandType.ValidateItems:
                    ExecuteMultipleItemValidation(context);
                    break;
                case CommandType.ValidateLastFound:
                    ExecuteValidateLastFound(context);
                    break;
                default:
                    throw new ExecutionException(string.Format(CultureInfo.InvariantCulture, "Unable to execute Validate command for command type {0}", this.CommandType));
            }
		}

	    #endregion
		
		#region Private instance methods

        /// <summary>
        /// Executes the multiple item validation.
        /// </summary>
        /// <param name="context">The context.</param>
        private void ExecuteMultipleItemValidation(IExecutionContext context)
        {
            if (this.PropertyType != PropertyType.SelectedItem)
            {
                throw new ArgumentOutOfRangeException(string.Format(CultureInfo.InvariantCulture, "Unable to preform multiple validation using unknown property type: {0}.", this.PropertyType));
            }

            bool foundAllItems = true;
            SelectList list = new SelectList(this.FindMethod.Execute(context));
            StringBuilder errorMessage = new StringBuilder();

            foreach (string expectedValue in this.ExpectedValues)
            {
                bool foundMatch = false;
                foreach (Option selectedOption in list.SelectedOptions)
                {
                    if (selectedOption.Value.Equals(expectedValue))
                    {
                        foundMatch = true;
                    }
                }
                
                if (!foundMatch)
                {
                    foundAllItems = false;
                    errorMessage.Append(string.Format(CultureInfo.InvariantCulture, "Expected value {0} was not found.\n", expectedValue));
                }
            }

            if (!foundAllItems)
            {
                throw new ValidateException(errorMessage.ToString());
            }
        }

        /// <summary>
        /// Executes the validate last found command.
        /// </summary>
        /// <param name="context">The context.</param>
        private void ExecuteValidateLastFound(IExecutionContext context)
        {
            this.ValidateStringValue(context.LastFoundValue);
        }

        /// <summary>
        /// Executes the single item validation.
        /// </summary>
        /// <param name="context">The context.</param>
        private void ExecuteSingleItemValidation(IExecutionContext context)
        {
            switch (this.PropertyType)
            {
                case PropertyType.Text:
                    if (this.FindMethod.MethodType == FindMethodTypes.UsingDocument)
                    {
                        // Validates the text of the document contains a specific value.                    
                        this.ValidateStringValue(context.Browser.HtmlDocument.body.innerText);
                    }
                    else
                    {
                        // Validates the text of a property, identified by the find method and find method arguments,
                        // contains a specific value.
                        this.ValidateStringValue(this.FindMethod.Execute(context).Text);
                    }

                    break;
                case PropertyType.Value:
                    this.ValidateStringValue(new WatiN.Core.TextField(this.FindMethod.Execute(context)).Value);
                    break;
                case Wax.PropertyType.SelectedItem:
                    this.ValidateStringValue(new WatiN.Core.SelectList(this.FindMethod.Execute(context)).SelectedOption.Value);
                    break;
                case Wax.PropertyType.Attribute:
                    this.ValidateStringValue(this.FindMethod.Execute(context).GetAttributeValue(this.AttributeName));
                    break;
                default:
                    throw new ArgumentOutOfRangeException(string.Format(CultureInfo.InvariantCulture, "Unable to validate property using unknown property type: {0}.", this.PropertyType));
            }
        }

		/// <summary>
		/// Validates the passed in <paramref name="value" /> and checks that it's the same as
		/// the <see cref="ExpectedValue" />.
		/// </summary>
		/// <exception cref="ValidateException">If the passed in <paramref name="value" /> is not the same as
		/// the <see cref="ExpectedValue" />.</exception>
		/// <param name="value">Value to assert.</param>
		private void ValidateStringValue(string value)
		{
            if (this.IsRegularExpression)
            {
                Regex regex = new Regex(this.ExpectedValue);
                if (!regex.Match(value).Success)
                {
                    throw new ValidateException(string.Format(CultureInfo.InvariantCulture, Resource.ValidationExceptionRegex, this.ExpectedValue, value));
                }
            }
			else if(!value.Contains(this.ExpectedValue))
			{
				throw new ValidateException(string.Format(CultureInfo.InvariantCulture, Resource.ValidationExceptionStringEquals, this.ExpectedValue, value));
			}
		}
		
		#endregion
	}
}
